SQSデッドレターキューにメッセージが入ったことを検知するメトリクスはどれが良いのか
デッドレターキュー(以下、DLQ)とは
正常に処理できないSQSメッセージを移動(退避)させるキューのことをいいます。この機能を使うと以下のようなメリットがあります。
- 問題のあるメッセージを分離して、問題の原因を調査できる。
- 元のキューのワーカーは、問題のあるメッセージを何度も受信してエラーし続けることを避けられる。そのため処理能力が向上する。
より詳細な情報は以下をご確認ください。
DLQにメッセージが入ったら知りたい
DLQにメッセージが入る=何かしらの問題が発生しているということなので、すぐに知りたいですよね。最低でも見逃すことは避けたいです。というわけで、CloudWatch AlarmでDLQにメッセージが入ったことを検知して、SNS経由でメール通知をします。
失敗例:NumberOfMessagesSentを使う
上記をTerraformで実装してみた例が以下です。
aws_cloudwatch_metric_alarmを使って、s3_put_object_notification_dlq
というキューに1分間に1件以上メッセージが送信されたらdlq_recieved_message_notification
というSNSトピックにメッセージを送ります。(EメールのSNSトピックサブスクリプションも別途作成します)
resource aws_cloudwatch_metric_alarm dlq_recieved_message { alarm_name = "dlq-recieved-message" comparison_operator = "GreaterThanOrEqualToThreshold" evaluation_periods = "1" metric_name = "NumberOfMessagesSent" namespace = "AWS/SQS" period = "60" statistic = "Sum" threshold = "1" alarm_description = "dead letter queue received message" alarm_actions = [aws_sns_topic.dlq_recieved_message_notification.arn] insufficient_data_actions = [] dimensions = { QueueName = aws_sqs_queue.s3_put_object_notification_dlq.name } }
使用するCloudWatchメトリクスは、以下ページを参考に NumberOfMessagesSent
を選びました。「キューに追加されたメッセージの数」というメトリクスなので適切そうです。
動作確認
このDLQはput-object-notification-queue
というキューのDLQです。動作確認のために最大受信数を1にしてすぐメッセージがDLQに移動するようにします。
まずは移動元のキューであるput-object-notification-queue
でメッセージを作成します。
コンソール上でのメッセージ表示も、そのメッセージの表示回数を加算します。というわけで先程作成したメッセージをコンソール上で表示します。
表示できました。
メッセージの表示回数がDLQのポリシーである最大受信数に達したので、DLQにメッセージが移動されました。
ここまで想定通り…あとはCloudWatch Alarmで検知してSNS経由でメールが送られてくるはずだ…と思っていたのですが、メールが送られてきません。
なぜ失敗したのか
確かにDLQはメッセージを受信しているのですが、NumberOfMessagesSent
CloudWatchメトリクスに変化がありません。そのためCloudWatch Alarmは何も反応しなかったのです。
NumberOfMessagesSent
CloudWatchメトリクスに変化がなかった理由ですが、DLQのドキュメントに以下記述がありました。
手動でデッドレターキューに送信したメッセージは、NumberOfMessagesSent メトリクスによってキャプチャされます。ただし、処理の試行が失敗した結果としてデッドレターキューにメッセージが送信された場合、メトリクスによってキャプチャされません。したがって、NumberOfMessagesSent と NumberOfMessagesReceived の値が異なることもあります。
ほう。ここからは私の推測ですが、どうやらキュー→DLQへのメッセージの移動は、
- DLQにメッセージ作成
- 元のキューからメッセージを削除
というより、
- 元のキューのメッセージをDLQへ移動する
という感じで、DLQ機能を使わないキュー間のメッセージ移動とは少し処理内容が違うようです。それは以下ドキュメントからも見て取れます。
メッセージの有効期限は、常に元のキュー追加のタイムスタンプに基づいています。メッセージが dead-letter queue に移動されると、キュー追加のタイムスタンプは変更されません。たとえば、メッセージがデッドレターキューに移動される前に元のキューに 1 日を費やし、デッドレターキューの保存期間が 4 日に設定されている場合、メッセージは 3 日後にデッドレターキューから削除されます。したがって、デッドレターキューの保持期間を元のキューの保持期間よりも長く設定することが、常にベストプラクティスとなります。
メッセージの有効期限は、元のキューのものを引き継ぎます。DLQに新メッセージ作成というより、元キューからメッセージを移動させてる感じがします。
成功例:ApproximateNumberOfMessagesVisibleを使う
というわけで、CloudWatch Alarmで使うメトリクスを変更しました。 ApproximateNumberOfMessagesVisible
=「キューから取得可能なメッセージの数」を使います。
resource aws_cloudwatch_metric_alarm dlq_recieved_message { alarm_name = "dlq-recieved-message" comparison_operator = "GreaterThanOrEqualToThreshold" evaluation_periods = "1" + metric_name = "ApproximateNumberOfMessagesVisible" - metric_name = "NumberOfMessagesSent" namespace = "AWS/SQS" period = "60" statistic = "Sum" threshold = "1" alarm_description = "dead letter queue received message" alarm_actions = [aws_sns_topic.dlq_recieved_message_notification.arn] insufficient_data_actions = [] dimensions = { QueueName = aws_sqs_queue.s3_put_object_notification_dlq.name } }
この設定で無事アラートメールを受信することができました。
まとめ
- SQSのデッドレターキューにメッセージが入ったことをアラートしたい場合、
NumberOfMessagesSent
メトリクスでは要件を満たせない。NumberOfMessagesSent
はDLQ機能によりキューに入ったメッセージをキャプチャしないため。
- 代わりに
ApproximateNumberOfMessagesVisible
などの別のメトリクスを使いましょう。